using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace net.veekee.common.reflection
{
    public class DLLInterop
    {
        private const long LOAD_LIBRARY_AS_DATAFILE = 0x00000002;

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern IntPtr LoadLibraryEx(String lpFileName, IntPtr hFile, uint dwFlags);

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern IntPtr LoadLibrary(String lpFileName);
        
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool FreeLibrary(IntPtr hModule);

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern IntPtr GetModuleHandle(String lpModuleName);

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool GetModuleHandleEx(int dwFlags, String lpModuleName, out IntPtr phModule);
        
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern IntPtr FindResource(IntPtr hModule, String lpName, String lpType);

        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern int LoadString(IntPtr hInstance, uint uID, StringBuilder lpBuffer, int nBufferMax);

        public static void LoadString(String dll, ref String container)
        {
            if (container.StartsWith("res://"))
                DLLInterop.LoadRESString(dll, ref container);
            if (container.StartsWith("@"))
                DLLInterop.LoadArobaseString(dll, ref container);
        }

        private static void LoadRESString(String dll, ref String container)
        {
            String[] references = container.Split(new char[] { '/' });
            if (dll == null)
            {
                if (references[2].IndexOf(System.IO.Path.DirectorySeparatorChar) == -1)
                    dll = Environment.GetFolderPath(Environment.SpecialFolder.System) + System.IO.Path.DirectorySeparatorChar + references[2];
            }
            String val = DLLInterop.GetString(dll, UInt32.Parse(references[4].Remove(0, 1)));
            if (val != null)
                container = val.Replace("&", "");
        }

        private static void LoadArobaseString(String dll, ref String container)
        {
            String[] references = container.Split(new char[] { ',' }, 2);
            if (dll == null)
                dll = references[0].Substring(1);
            String val = DLLInterop.GetString(dll, UInt32.Parse(references[1].Substring(1)));
            if (val != null)
                container = val.Replace("&", "");
        }

        private static String GetString(String dllname, uint id)
        {
            try
            {
                IntPtr hWnd = IntPtr.Zero;
                hWnd = DLLInterop.LoadLibraryEx(dllname, IntPtr.Zero, 2);
                //System.Diagnostics.Trace.WriteLine(Marshal.GetLastWin32Error());
                if (hWnd == IntPtr.Zero)
                    return null;
                StringBuilder buffer = new StringBuilder(200);
                DLLInterop.LoadString(hWnd, id, buffer, 200);
                DLLInterop.FreeLibrary(hWnd);
                return buffer.ToString();
            }
            catch
            {
            }
            return null;
        }

        public static DateTime GetAssemblyCompilationTime(System.Reflection.Assembly a)
        {
            return DLLInterop.GetAssemblyCompilationTime(a.Location);
        }

        public static DateTime GetAssemblyCompilationTime(String filename)
        {
            int PeHeaderOffset = 60;
            int LinkerTimestampOffset = 8;
            int SecondsSince1970 = 0;
            int i = 0;
            byte[] b = new byte[2047];
            System.IO.Stream s = null;
            try {
                s = new System.IO.FileStream(filename, System.IO.FileMode.Open, System.IO.FileAccess.Read);
                s.Read(b, 0, 2047);
            } finally {
                if(s != null)
                    s.Close();
            }
            
            i = BitConverter.ToInt32(b, PeHeaderOffset);
            SecondsSince1970 = BitConverter.ToInt32(b, i + LinkerTimestampOffset);
            DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0);
            dt = dt.AddSeconds(SecondsSince1970);
            dt = dt.AddHours(TimeZone.CurrentTimeZone.GetUtcOffset(dt).Hours);
            return dt;
        }
    }
}
